/*
	Class: DocElemInclude

		This class handles the <include /> tag.

		We're playing pretty footloose with the include tag at least as an
		analogy with HTML. But anyway, we should support these rel values:

		  stylesheet
		  inline		- default, contents to include in the document itself
		  assets		- external assets, i.e., a SWF of assets

		And for type we should support these values:

		  text/css						- only for stylesheet
		  text/xml						- for stylesheet or inline
		  application/x-shockwave-flash	- only for assets
		
		The blocking attribute delays parsing until this asset is fully loaded
		when set to "true".

*/
package com.frontalcode
{
	import flash.events.*;

	/**
	 * The DocElemInclude class handles the <include /> tag.
	 */
    public class DocElemInclude extends DocumentElement
    {
		/**
		 * @private
		 */
		internal var asset : Asset;
		internal var loader : ContainerXMLLoader;
		private var assetsBlockParse : Boolean;
		private var loadingComplete : Boolean = false;

		/**
		 * Creates a new DocElemInclude instance.
		 * 
		 * @param document	The document this element belongs to.
		 * @param parent	The element's parent in the document tree.
		 * @param node		The XML node this element is associated with.
		 * @param options	Options affecting the behavior of this instance.
		 * 					(This object does not have built-in support for any
		 * 					options.)
		 */
		public function DocElemInclude ( document : Document, parent : DocumentElement, node : XML, options : Object = null )
		{
			super ( document, parent, node, options );
			
			assetsBlockParse = getAttribute ( "blocking" ) == "true";

			if ( getAttribute ( "rel" ).toLowerCase ( ) == "inline" )
			{
				loader = document.load ( AssetManager.getInstance ( ).getFullURL ( getAttribute ( "src" ), getStyle ( "base-url" ) ), parent, node );
				addListener ( loader, Event.COMPLETE, loadEventHandler, false, -1000 );
				addListener ( loader, IOErrorEvent.IO_ERROR, loadEventHandler, false, -1000 );
				addListener ( loader, SecurityErrorEvent.SECURITY_ERROR, loadEventHandler, false, -1000 );
				addListener ( loader, ProgressEvent.PROGRESS, loadEventHandler );
				document.processDelayed = true;
			}
			else if ( getAttribute ( "rel" ).toLowerCase ( ) == "assets" && assetsBlockParse )
			{
				asset = document.externalAssetManager.load ( getAttribute ( "src" ), getStyle ( "base-url" ), getStyle ( "check-policy-file" ), getStyle ( "policy-file-url" ), getStyle ( "load-in-current-app-and-sec-domains" ) == null ? true : getStyle ( "load-in-current-app-and-sec-domains" ) );
				
				addListener ( asset, FrontalEvent.LOADED, assetLoadHandler );
				addListener ( asset.loader.contentLoaderInfo, ProgressEvent.PROGRESS, assetLoadHandler );

				document.processDelayed = true;
			}

			processesOwnContent = true;
		}

		/**
		 * @private
		 */
		internal function loadEventHandler ( event : Event ) : void
		{
			dispatchEvent ( new FrontalEvent ( FrontalEvent.PROGRESS ) );
			
			if ( event.type != ProgressEvent.PROGRESS )
			{
				removeListener ( loader, Event.COMPLETE, loadEventHandler );
				removeListener ( loader, IOErrorEvent.IO_ERROR, loadEventHandler );
				removeListener ( loader, SecurityErrorEvent.SECURITY_ERROR, loadEventHandler );
				removeListener ( loader, ProgressEvent.PROGRESS, loadEventHandler );
	
				loadingComplete = true;
				
				// Restart the delayed document processing.
				//
				document.processDelayed = false;
				document.parseDefinition ( parent, node );
			}
		}

		/**
		 * @private
		 */
		override public function applyStyles ( ) : void
		{
			// We want a reset to know when to prepare our asset.
			//
			if ( getStyle ( "wants-reset" ) == null ) setStyle ( "wants-reset", true );

			super.applyStyles ( );
		}

		/**
		 * @private
		 */
		override public function prepareAssets ( ) : void
		{
			super.prepareAssets ( );

			if ( asset == null )
			{
				if ( getAttribute ( "rel" ).toLowerCase ( ) == "assets" )
				{
					asset = document.externalAssetManager.load ( getAttribute ( "src" ), getStyle ( "base-url" ), getStyle ( "check-policy-file" ), getStyle ( "policy-file-url" ), getStyle ( "load-in-current-app-and-sec-domains" ) == null ? true : getStyle ( "load-in-current-app-and-sec-domains" ) );
					addListener ( asset, FrontalEvent.LOADED, assetLoadHandler );
					addListener ( asset.loader.contentLoaderInfo, ProgressEvent.PROGRESS, assetLoadHandler );
				}
			}
			else
			{
				AssetManager.getInstance ( ).demandObject ( asset );
			}
		}

		/*
			Function: assetLoadHandler

				Called when the external asset loads.

		*/
		/**
		 * @private
		 */
		public function assetLoadHandler ( event : Event ) : void
		{
			dispatchEvent ( new FrontalEvent ( FrontalEvent.PROGRESS ) );
			
			if ( event.type != ProgressEvent.PROGRESS )
			{
				removeListener ( asset, FrontalEvent.LOADED, assetLoadHandler );
				removeListener ( asset.loader.contentLoaderInfo, ProgressEvent.PROGRESS, assetLoadHandler );
				
				loadingComplete = true;

				// When our external asset loads, search it for font classes and
				// register the ones we find. (Registering will cause dependent
				// containers to re-render themselves.)
				//
				var fontClasses : Array = getAttribute ( "fontClasses" ).split ( /\s*,\s*/ );
				for ( var i : uint = 0; i < fontClasses.length; i++ )
				{
					try
					{
						var fontClassRef : Class = ( event.target as Asset ).loader.contentLoaderInfo.applicationDomain.getDefinition ( fontClasses [ i ] ) as Class;
						document.fontManager.registerFont ( fontClassRef );
					}
					catch ( e : Error )
					{
					}
				}
	
				// Import any classes declared with <import /> inside this node to the global variable space.
				// If a symbol synonymous with the class to be imported already exists in the global space,
				// we just ignore the import.
				//
				for ( var c : uint; c < node.children ( ).length ( ); c++ )
				{
					if ( node.child ( c ).name ( ) == "import" )
					{
						var fqName : String = node.child ( c ).text ( );
	
						try
						{
							var classRef : Class 	= document.externalAssetManager.getDefinition ( fqName, null, getAttribute ( "src" ) );
							var className : String	= fqName.substring ( fqName.lastIndexOf ( "." ) + 1, fqName.length );
	
							var docGlobals : Object = ActionScriptInterpreter.getDocumentGlobalScope ( document );
							if ( ! docGlobals [ className ] )
							{
								docGlobals [ className ] = classRef;
							}
						}
						catch ( e : Error )
						{
							Debugger.msg ( 'DocElemInclude: ' + e.toString ( ) );
						}
					}
				}
				
				if ( assetsBlockParse )
				{
					document.processDelayed = false;
					document.parseDefinition ( parent, node );	
				}
			}
		}
		
		/*
			Function: calculateLoadProgress
			
				Add our load progress to the total progress.
				
			Parameters:
			
				progress	- The progress statistics calculated so far.
		*/
		/**
		 * @private
		 */
		override public function calculateLoadProgress ( progress : Object ) : void 
		{ 
			var entry : Object = {
					docElem: this,
					bytesLoaded: 0,
					bytesTotal: 0,
					fraction: 0		// This is not necessarily bytesLoaded / bytesTotal because initially bytesTotal may be zero.
				};
				
			if ( asset != null )
			{
				entry.bytesLoaded = asset.bytesLoaded;
				entry.bytesTotal = asset.bytesTotal;
			}
			else if ( loader != null )
			{
				entry.bytesLoaded = loader.bytesLoaded;
				entry.bytesTotal = loader.bytesTotal;
			}
			
			// XXX Saw Apache report an incorrect Content-Length for a small XML
			// file which meant bytesLoaded was always less than bytesTotal even
			// after loading was complete.
			//
			if ( loadingComplete ) 
			{
				entry.bytesTotal = entry.bytesLoaded;
				entry.fraction = 1; // In case the file was zero bytes.
			}
			
			// XXX Strangeness on IE where bytesLoaded is a non-zero value but
			// bytesTotal is zero.
			//
			if ( entry.bytesTotal == 0 && entry.bytesLoaded > 0 ) entry.bytesTotal = entry.bytesLoaded;
			
			if ( entry.bytesTotal > 0 ) entry.fraction = entry.bytesLoaded / entry.bytesTotal;
			
			progress.entries.push ( entry );
			
			super.calculateLoadProgress ( progress );
		}
	}
}